package com.example.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.common.Vo.UserVO;
import com.example.entity.*;
import com.example.mapper.UserMapper;
import com.example.security.MyUserDetails;
import com.example.service.*;
import com.example.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author ptuDai
 * @since 2021-09-13
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private IRoleService roleService;

    @Autowired
    private IMenuService menuService;

    @Autowired
    private IUserDepartmentService userDepartmentService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private IRoleMenuService roleMenuService;

    @Override
    public IPage<UserVO> selectUserPage(IPage<UserVO> page, String name, Integer departmentId) {
        // 不进行 count sql 优化，解决 MP 无法自动优化 SQL 问题，这时候你需要自己查询 count 部分
        // page.setOptimizeCountSql(false);
        // 当 total 为小于 0 或者设置 setSearchCount(false) 分页插件不会进行 count 查询
        // 要点!! 分页返回的对象与传入的对象是同一个
        IPage<UserVO> userVOs = this.userMapper.selectPageVo(page, name, departmentId);
        userVOs.getRecords().forEach(userVO -> {
            userVO.setRoles(roleService.listRoleByUserId(userVO.getId()));
        });
        return userVOs;
    }

    @Override
    public MyUserDetails getUserDetailsByUserName(String userName) {
        // 优先走缓存
        if (redisUtil.hasKey("userDetails:" + userName)) {
            final String jsonString = (String) redisUtil.get("userDetails:" + userName);
            return JSON.parseObject(jsonString, MyUserDetails.class);
        }
        // 缓存找不到走数据库查询，查询结果不为空存入缓存中
        final MyUserDetails userDetails = userMapper.getUserDetailsByUserName(userName);
        if (userDetails != null) {
            String stringUserDetails = JSON.toJSONString(userDetails);
            redisUtil.set("userDetails:" + userName, stringUserDetails, 60 * 60);
        }
        return userDetails;
    }

    @Override
    public boolean deleteUsers(List<Integer> userIds) {
        List<User> users = this.listByIds(userIds);
        QueryWrapper<UserDepartment> wrapper = new QueryWrapper<>();
        wrapper.select("id").in("user_id", userIds);
        final List<Integer> ids = userDepartmentService.listObjs(wrapper, type -> Integer.valueOf(type.toString()));
        this.userDepartmentService.removeByIds(ids);
        if (this.removeByIds(userIds)) {
            users.forEach(user -> {
                redisService.clearUserAuthoritiesInfo(user.getId());
                System.out.println("清除：" + user.getUserName() + "缓存");
            });
            return true;
        } else {
            return false;
        }
    }

    @Override
    public User getByUserName(String userName) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("user_name", userName);
        return this.getOne(wrapper);
    }

    @Override
    public UserVO getUserInfoByUserName(String name) {
        UserVO userVO = this.userMapper.getUserInfoByUserName(name);
        return userVO;
    }

    @Override
    public List<Menu> getNavMenus(Integer id) {
        List<Role> roles = roleService.list(new QueryWrapper<Role>().inSql("id", "select role_id from user_role where user_id = " + id).eq("status",1));
        List<RoleMenu> roleMenus = roleMenuService.list(new QueryWrapper<RoleMenu>().in("role_id",roles.stream().map(Role::getId).collect(Collectors.toList())));
        List<Menu> menus = menuService.list(new QueryWrapper<Menu>().in("id",roleMenus.stream().map(RoleMenu::getMenuId).collect(Collectors.toList())).orderByDesc("order_num"));
        return menus;
    }

    @Override
    public List<User> listByMenuId(Integer menuId) {
        return userMapper.listByMenuId(menuId);
    }

    @Override
    public String getUserNameById(Integer userId) {
        return userMapper.getUserNameById(userId);
    }

    @Override
    public Integer getIdByUserName(String userName) {
        String key = "idBy" + userName;
        Integer id;
        if (redisUtil.hasKey(key)) {
            id = (Integer) redisUtil.get(key);
        } else {
            id = userMapper.getIdByUserName(userName);
            redisUtil.set("idBy" + userName, id, 60 * 60);
        }
        return id;
    }

    @Override
    public String getUserAuthorityInfo(String userName) {
        String authority = null;
        if (redisUtil.hasKey("GrantedAuthority:" + userName)) {
            // 优先从缓存获取
            authority = (String) redisUtil.get("GrantedAuthority:" + userName);
        } else {
            Integer userId = this.getIdByUserName(userName);
            List<Role> roles = roleService.list(new QueryWrapper<Role>().inSql("id", "select role_id from user_role where user_id = " + userId).eq("status",1));
            List<RoleMenu> roleMenus = roleMenuService.list(new QueryWrapper<RoleMenu>().in("role_id",roles.stream().map(Role::getId).collect(Collectors.toList())));
            List<Menu> menus = menuService.list(new QueryWrapper<Menu>().in("id",roleMenus.stream().map(RoleMenu::getMenuId).collect(Collectors.toList())).orderByDesc("order_num"));
            String roleNames = roles.stream().map(r -> "ROLE_" + r.getRoleCode()).collect(Collectors.joining(","));
            String permNames = menus.stream().map(m -> m.getPerms()).collect(Collectors.joining(","));
            authority = roleNames.concat(",").concat(permNames);
            UserServiceImpl.log.info("用户ID - {} ---拥有的权限：{}", userId, authority);
            redisUtil.set("GrantedAuthority:" + userName, authority, 60 * 60);
        }
        return authority;
    }

}
